import { log_error, log } from "./log.js"
import { indexedDB, IDBTransaction, IDBKeyRange } from "./global.js";
import dep from "./dep.js";
import { DBInformation } from "./tool.js"

const isArray = function (data) { return Object.prototype.toString.call(data) === "[object Array]" }
const isObject = function (data) { return Object.prototype.toString.call(data) === "[object Object]" }

class DB {
  #DBDatabase;
  constructor({ DBname, version }) {
    this.DBname = DBname;
    this.version = version;
    this.table = []
    this.DBInformation = null
    this._dep_ = new dep()
  }
 async open(opt) {
    let success = () => { },
      error = () => { };
    if (opt) {
      success = opt.success ? opt.success : success,
        error = opt.error ? opt.error : error;
    }
    if (this.table.length === 0) {
      log_error("打开前要先用add_table添加表");
      return
    }
    if (typeof success !== "function") {
      log_error("open中success必须是一个function类型");
      return;
    }
    const OpenDBRequest =  indexedDB.open(this.DBname, this.version);

    OpenDBRequest.onsuccess = (e) => {
      this.DBInformation = DBInformation(e)
      this.#DBDatabase = OpenDBRequest.result
      success(this.#DBDatabase)
      this._dep_.notify();

        this.#DBDatabase.addEventListener("versionchange", () => {
          console.log("发生数据库更改;");
        });
        this.#DBDatabase.addEventListener("close", () => {
          console.log("数据库连接已关闭");
        });
    }

    OpenDBRequest.onerror = (e) => {
      error(e.currentTarget.error.message);
    }

    OpenDBRequest.onupgradeneeded = (e) => {
      this.#DBDatabase = OpenDBRequest.result

      for (const item of this.table) {
        this.creat_table(item)
      }

    }
  
  }

  add({ tableName, data }) {
    if (!(isArray(data) || isObject(data))) {
      log_error("in insert，data type is Object or Array");
      return;
    }
    this.__action(() => {
      return this.__create_transaction(
        tableName,
        "readwrite",
        (store) => {
          isArray(data) ? data.forEach(item => store.add(item)) : store.add(data)
          log(`${tableName} 已经存入成功`)
        }
      )
    })

  }
  updata({ tableName, handler, condition = () => true }) {
    return this.__action(() => {
      return this.__create_transaction(
        tableName,
        "readwrite",
        (store) => {
          return new Promise((resolve, reject) => {
            try {
              let index = 0,
                res = []
              this.__openCursor(store, async (cursor) => {
                if (cursor) {
                  let curValue = cursor.value;
                  if (await condition(curValue, index)) {
                    await handler(curValue);
                    cursor.update(curValue);
                    res.push(curValue)
                  }
                  cursor.continue();
                  index++;
                } else {
                  resolve(res)
                }
              })
            } catch (error) {
              reject(error)
            }
          })
        }
      )
    }
    )
  }
  query({ tableName, condition = () => true }) {
    return this.__action(() => {
      return this.__create_transaction(
        tableName,
        "readwrite",
        (store) => {
          return new Promise((resolve, reject) => {
            try {
              let index = 0,
                res = [];
              this.__openCursor(store, async (Cursor) => {
                if (Cursor) {
                  let curvalue = Cursor.value;
                  if (await condition(curvalue)) {
                    res.push(curvalue)

                  }
                  Cursor.continue();
                  index++

                } else {
                  resolve(res)
                }
              })
            } catch (error) {
              reject(error)
            }
          })
        }
      )
    })
  }
  delete({ tableName, condition = () => true }) {
    return this.__action(() => {
      return this.__create_transaction(
        tableName,
        "readwrite",
        (store) => {
          new Promise((resolve, reject) => {
            try {
              let index = 0,
                res = [];
              this.__openCursor(store, async (Cursor) => {
                if (Cursor) {
                  let Curvalue = Cursor.value;
                  if (await condition(Curvalue)) {
                    Cursor.delete(Curvalue);
                    res.push(Curvalue);
                  }
                  Cursor.continue()
                  index++
                } else {
                  resolve(res)
                }
              })
            } catch (error) {
              reject(error)
            }
          })
        }
      )
    })
  }
  transaction({ tableName, type = "readwrite" }) {
    return this.__action(() => {
      return this.#DBDatabase.transaction(tableName, type).objectStore(tableName);
    })
  }
  getDB() {
    return this.__action(() => this.#DBDatabase)
  }
  creat_table({ tableName, option, indexs = [] }) {
    if (!this.#DBDatabase.objectStoreNames.contains(tableName)) {
      let store = this.#DBDatabase.createObjectStore(tableName, option)
      if (indexs.length > 0) {
        for (let indexItem of indexs) {
          this.creat_index(store, indexItem)
        }
      }
    }

  }
  creat_index(store, { key, option }) {
    store.createIndex(key, key, option)
  }
  add_table(tableOption = {}) {
    this.table.push(tableOption)
  }
  __action(handler) {
    return new Promise((resolve, reject) => {
      const action = async () => {
        try {
          let result = await handler()
          resolve(result)
        } catch (error) {
          reject(error)
        }
      }
      if (!this.#DBDatabase) {
        this._dep_.add(action)
      } else {
        action()
      }
    })

  }
  __create_transaction(tableName, type = "readwrite", handler) {
    return new Promise(async (resolve, reject) => {
      if (!tableName || !type) {
        reject()
        throw new Error(
          "in __create_transaction,tableName and type is required"
        );
      }
      const DBTransaction = this.#DBDatabase.transaction(tableName, type);
      const Store = DBTransaction.objectStore(tableName)
      let data
      try {
        data = await handler(Store)
      } catch (error) {
        reject(error)
      }

      DBTransaction.oncomplete = function (event) {
        resolve({ data, event });
      }
      DBTransaction.onerror = function (event) {
        reject({
          msg: event.target.error,
          event
        })
      }
    })
  }
  __openCursor(store, callback) {
    let request = store.openCursor()
    request.onsuccess = function () {
      let cursor = request.result;
      callback(cursor)
    }
  }
  getDBInformation() {
    if (!this.DBInformation) {
      log_error("先打开indexDB库")
      return
    }
    return this.DBInformation
  }
}

export default DB